#pragma once

#include <iostream>
#include <string>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstdlib>
#include "Common.hpp"
#include "log.hpp"
#include "InetAddr.hpp"

namespace SocketModule
{
    const static int defaultsockfd = -1;

    // Udp和Tcp都需要套接字。
    // 直接用纯虚函数。
    class Socket 
    {
    public:
        virtual ~Socket() {}
        virtual void SocketOrDie() = 0;
        virtual void BindOrDie(uint16_t port) = 0;
        virtual void ListenOrDie(int backlog) = 0;
        virtual std::shared_ptr<Socket> Accept(InetAddr *client) = 0;
        virtual void Close() = 0;

        public:
        void BuildTcpSocketMethod(uint16_t port, int backlog = gbacklog)
        {
            SocketOrDie();
            BindOrDie(port);
            ListenOrDie(backlog);
        }
    };

    class Tcp : public Socket
    {
    public:
        Tcp(int socket = defaultsockfd)
            :_sockfd(socket)
        {
            ;
        }
        void SocketOrDie() override
        {
            _sockfd = ::socket(AF_INET,SOCK_STREAM,0);

            if(_sockfd < 0)
            {
                LogSpace::LOG(LogSpace::LogLevel::FATAL) << "SOCK_ERROR";
                ::exit(SOCK_ERROR);
            }
            LogSpace::LOG(LogSpace::LogLevel::INFO) << "Sock Success";
        }
        void BindOrDie(uint16_t port) override
        {
            InetAddrModule::InetAddr addr(port);
            int n = ::bind(_sockfd,addr.NetAddrPtr(),addr.NetAddrLen());
            if(n < 0)
            {
                LogSpace::LOG(LogSpace::LogLevel::FATAL) << "bind ERROR";
                ::exit(BIND_ERROR);
            }
            LogSpace::LOG(LogSpace::LogLevel::INFO) << "bind Success!";
        }
        void ListenOrDie(int backlog)override
        {
            int n = ::listen(_sockfd,backlog);
            if(n < 0)
            {
                LogSpace::LOG(LogSpace::LogLevel::FATAL) << "Listen ERROR";
                ::exit(LISTEN_ERROR);
            }
            LogSpace::LOG(LogSpace::LogLevel::INFO) << "Listen Success!";
        }
        std::shared_ptr<Socket> Accept(InetAddrModule::InetAddr *client) override
        {
            struct sockaddr_in peer;
            socklen_t len = sizoef(peer);
            int fd = ::accept(_sockfd,(const struct sockaddr *)&peer,&len);
            if(fd < 0)
            {
                LogSpace::LOG(LogSpace::LogLevel::FATAL) << "Accept ERROR";
                ::exit(ACCEPT_ERROR);
            }
            LogSpace::LOG(LogSpace::LogLevel::INFO) << "Accept Success!";
            client->SetAddr(peer);
            return std::make_shared<Socket>(fd);
        }
        void Close()
        {
            if(_sockfd >= 0)
                ::close(_sockfd);
        }
    private:
        int _sockfd;    // _sockfd   ,     listensockfd   ,    sockfd
    }
}